"""
pagamiago - free software script to send PayPal payments

Copyright (C) 2018 Alyssa Rosenzweig

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <https://www.gnu.org/licenses/>.
"""

import sys
import json
import requests
import urllib.parse
import time
import random

rsession = requests.Session()


class ProgressBar:
    def __init__(self):
        pass

    def update(self):
        self.complete += 1

        sys.stdout.write("\r" +
                         self.label +
                         ": [" +
                         (self.complete *
                          "*") +
                         ((self.total -
                           self.complete) *
                          ".") +
                         "]")

        if self.total == self.complete:
            sys.stdout.write("\n")


class SilentProgressBar:
    def __init__(self):
        pass

    def update(self):
        pass


class RequestSession:
    def __init__(self, base, debug, mock):
        self.base = base
        self.debug = debug
        self.mock = mock

        self.progress_bar = (SilentProgressBar if debug else ProgressBar)()
        self.headers = {
            "User-Agent": "Mozilla/5.0 (Linux; Android 7.0; SM-G892A Build/NRD90M; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/60.0.3112.107 Mobile Safari/537.36",
            "X-Requested-With": "XMLHttpRequest"}

        self.meta = None

    def set_module(self, referer, label, count):
        self.referer = referer

        self.progress_bar.label = label
        self.progress_bar.total = count
        self.progress_bar.complete = 0

    def mini_dumps(self, data):
        if isinstance(data, dict):
            return json.dumps(data, separators=(",", ":"))
        else:
            return data

    def request(
            self,
            url,
            is_post,
            data,
            mock_resp=None,
            plain_headers=False,
            like_form=False):
        url = self.base + url

        # Construct data, both for mock and for requests

        if self.meta:
            data["meta"] = self.meta

        if like_form or not is_post:
            print_data = urllib.parse.urlencode(
                {k: self.mini_dumps(v) for (k, v) in data.items()})

            if not is_post:
                url = url + (("?" + print_data)
                             if (len(print_data) > 0) else "")
        else:
            print_data = self.mini_dumps(data)

        # Construct headers

        headers = {
            "Referer": self.base + self.referer,
        }

        headers.update(self.headers)

        # Strip X-stuff for non-JavaScript requests

        if plain_headers:
            headers = {k: headers[k]
                       for k in headers if not k.upper()[0:2] == "X-"}

        # Log request if needed

        if self.debug:
            print(("POST" if is_post else "GET") + " " + url)

            if is_post:
                print("")
                print(print_data)

            print("")

        # Actuate request
        resp = ""

        # Gaussian sleep to be polite
        if not self.mock:
            time.sleep(abs(random.gauss(1.5, 1)))

        if self.mock:
            if mock_resp is not None:
                with open(mock_resp, "rb") as f:
                    resp = f.read().decode("utf-8")
        else:
            if is_post:
                if like_form:
                    r = rsession.post(url, data=data, headers=headers)
                else:
                    r = rsession.post(url, json=data, headers=headers)
            else:
                r = rsession.get(url, headers=headers)

            if r.status_code >= 400 and r.status_code < 500:
                die("Invalid response with error code " + r.status_code +
                    ". This is probably a bug in pagamigo.", guru=r.text)

            if r.status_code >= 500:
                print("Warning: " + str(r.status_code) + " error code?\n")

            # Ping-pong CSRF if necessary
            self.headers["x-csrf-jwt"] = r.headers.get("x-csrf-jwt")

            resp = r.text

        self.progress_bar.update()

        # Parse as JSON if applicable
        return json.loads(resp) if (len(resp) and resp[0] == "{") else resp
